package com.hzw.saas.common.storage.rsync;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.zip.Checksum;

import com.hzw.saas.common.storage.enums.ChecksumModeEnum;
import com.hzw.saas.common.storage.util.ChecksumUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.crypto.digest.DigestUtil;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

/**
 * @author EZ09
 * @since 07/08/2021
 */
@UtilityClass
@Slf4j
public class RsyncFileUtil {

    private final static int BUFFER_SIZE = 8192;

    public static int readBlock(File srcFile, long pos, int length, OutputStream ous) throws IOException {
        Assert.isTrue(pos >= 0);
        Assert.isTrue(length >= 1);
        try (RandomAccessFile raf = new RandomAccessFile(srcFile, "r")) {
            byte[] buffer = new byte[length];
            int read = RsyncIOUtil.readBuffer(raf, pos, buffer, 0, length);
            ous.write(buffer, 0, read);
            ous.flush();
            return read;
        }
    }

    public static OriginBlock calcBlockChecksum(File srcFile, long pos, int length, Integer checksumMode) throws IOException {
        Assert.isTrue(pos >= 0);
        Assert.isTrue(length >= 1);
        ChecksumModeEnum checksumModeEnum = ChecksumModeEnum.validInstance(checksumMode);
        try (RandomAccessFile raf = new RandomAccessFile(srcFile, "r")) {
            byte[] buffer = new byte[length];
            int read = RsyncIOUtil.readBuffer(raf, pos, buffer, 0, length);
            OriginBlock originBlock = new OriginBlock(pos, read);
            originBlock.calcChecksum(buffer, 0, checksumMode);
            return originBlock;
        }
    }

    public static List<OriginBlock> calcBlockChecksum(File srcFile, List<Long> posList, int length, Integer checksumMode) throws IOException {
        Assert.isTrue(length >= 1);
        List<OriginBlock> result = new ArrayList<>();
        if (CollectionUtil.isEmpty(posList)) {
            return result;
        }
        ChecksumModeEnum checksumModeEnum = ChecksumModeEnum.validInstance(checksumMode);
        try (RandomAccessFile raf = new RandomAccessFile(srcFile, "r")) {
            byte[] buffer = new byte[length];
            for (Long pos : posList) {
                Assert.isTrue(Objects.nonNull(pos) && pos > 0);
                int read = RsyncIOUtil.readBuffer(raf, pos, buffer, 0, length);
                OriginBlock originBlock = new OriginBlock(pos, read);
                originBlock.calcChecksum(buffer, 0, checksumMode);
                result.add(originBlock);
            }
            return result;
        }
    }

    public static File mergeFile(File srcFile, File destFile, String blockSupPath, List<DiffBlock> blockDiffList) throws IOException {
        log.debug("开始进行Rsync文件合并({} -> {})...", srcFile, destFile);
        File blockFolder = FileUtil.file(blockSupPath);
        long start = SystemClock.now();
        try (RandomAccessFile srcRaf = new RandomAccessFile(srcFile, "r");
             OutputStream destOus = FileUtil.getOutputStream(destFile)) {
            int matched = 0;
            int notMatched = 0;
            byte[] buffer = new byte[BUFFER_SIZE];
            int bufferStart = 0;
            for (DiffBlock blockDiff : blockDiffList) {
                boolean isOrigin = Objects.nonNull(blockDiff.getIsOrigin()) && blockDiff.getIsOrigin() > 0;
                if (isOrigin) {
                    log.debug("合并匹配块({pos: {}, length: {}})...", blockDiff.getBlockPos(), blockDiff.getBlockLength());
                    matched++;
                    int length = blockDiff.getBlockLength();
                    long pos = blockDiff.getBlockPos();
                    while (length > 0) {
                        int readLength = RsyncIOUtil.readBuffer(srcRaf, pos, buffer, bufferStart, Math.min(length, BUFFER_SIZE));
                        destOus.write(buffer, bufferStart, readLength);
                        length -= readLength;
                        pos += readLength;
                    }
                } else {
                    log.debug("合并差异块({id: {})...", blockDiff.getBlockId());
                    notMatched++;
                    File fileDiff = FileUtil.file(blockFolder, blockDiff.getBlockId());
                    Assert.isTrue(fileDiff.exists(), () -> new FileNotFoundException(String.format("差异块(%s)未找到", blockDiff.getBlockId())));
                    byte[] blockDiffBytes = FileUtil.readBytes(fileDiff);
                    destOus.write(blockDiffBytes);
                }
            }
            destOus.flush();
            log.debug("Rsync merge finished, matched: {}, not matched: {}", matched, notMatched);
            long cost = SystemClock.now() - start;
            log.debug("Rsync合并完成({} -> {}), cost: {}", srcFile, destFile, cost);
            return destFile;
        }
    }

}
